/*
#Copyright (c) 2013, Philippe Bordron <philippe.bordron@gmail.com>
#
# This file is part of SIPPER.
#
# DCC is free software: you can redistribute it and/or modify
# it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DCC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU LESSER GENERAL PUBLIC LICENSE for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# along with SIPPER.  If not, see <http://www.gnu.org/licenses/>
*/
package combi.wip.sipper.io.metacyc;


import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;

import org.jgrapht.graph.DefaultEdge;

import combi.sipper.io.InFlatFile;
import combi.wip.sipper.io.IOMetabolicNetworkWithEnzymes;
import combi.wip.sipper.io.Profil;
import combi.wip.sipper.modeling.Protein;
import combi.wip.sipper.modeling.metabolicNetworks.FilteredPathway;
import combi.wip.sipper.modeling.metabolicNetworks.ImplOfFilteredMetabolicNetworkWithEnzymes;
import combi.wip.sipper.modeling.metabolicNetworks.ImplOfMetabolicNetworkWithEnzymes;
import combi.wip.sipper.modeling.metabolicNetworks.MetabolicNetworkWithEnzymes;
import combi.wip.sipper.modeling.metabolicNetworks.Pathway;
import combi.wip.sipper.modeling.reactions.Reaction;
import combi.wip.sipper.modeling.reactions.ReactionUtil;

public class IOMetabolicNetworkWithEnzymesFromMetaCyc extends IOMetabolicNetworkWithEnzymes<String, Reaction<String>,Protein>{

	protected String fileCompounds;
	protected String fileReactions;
	protected String fileEnzReactions;
	protected String fileProteins;
	protected MetabolicNetworkWithEnzymes<String,Reaction<String>,Protein> meta;
	protected Map<String, Collection<Reaction<String>>> pathways;

	protected static String LEFT = "LEFT";
	protected static String RIGHT = "RIGHT";
	protected static String REACTION_DIRECTION = "REACTION-DIRECTION";
	protected static String LR = "LEFT-TO-RIGHT";
	protected static String RL = "RIGHT-TO-LEFT";
	protected static String BOTH = "BOTH";

	protected static String REACTION = "REACTION";
	protected static String ENZYME = "ENZYME";
	protected static String IN_PATHWAY = "IN-PATHWAY";

	public IOMetabolicNetworkWithEnzymesFromMetaCyc(String fileCompounds, String fileReactions, String fileEnzReactions, String fileProteins)
	{
		this.fileCompounds = fileCompounds;
		this.fileReactions = fileReactions;
		this.fileEnzReactions = fileEnzReactions;
		this.fileProteins = fileProteins;
		this.meta = new ImplOfMetabolicNetworkWithEnzymes<String,Reaction<String>,Protein>();
		this.pathways = new HashMap<String, Collection<Reaction<String>>>();
	}


	@Override
	public void load() throws IOException {
		Map<String, Map<String, Collection<String>>> compounds = new HashMap<String, Map<String, Collection<String>>>();
		Map<String, Map<String, Collection<String>>> reactions = new HashMap<String, Map<String, Collection<String>>>();
		Map<String, Map<String, Collection<String>>> enzrxns = new HashMap<String, Map<String, Collection<String>>>();
		InFlatFile compoundsIn = new InFlatFile(fileCompounds);
		InFlatFile reactionsIn = new InFlatFile(fileReactions);
		InFlatFile enzRxnsIn = new InFlatFile(fileEnzReactions);
		compounds = compoundsIn.readFile();
		reactions = reactionsIn.readFile();
		enzrxns = enzRxnsIn.readFile();

		Map<String, Protein> idToProtein = new HashMap<String, Protein>();
		IOProteinsFromMetaCyc loadProteins = new IOProteinsFromMetaCyc(fileProteins);
		idToProtein = loadProteins.load();
		
		for (String c : compounds.keySet())
		{
			meta.addCompound(c);
		}
		for (Entry<String, Map<String, Collection<String>>> entry : reactions.entrySet())
		{
			Collection<String> leftPart = entry.getValue().get(LEFT);
			Collection<String> rightPart = entry.getValue().get(RIGHT);
			Collection<String> orientation = entry.getValue().get(REACTION_DIRECTION);
			ReactionUtil.Orientation dir = ReactionUtil.Orientation.UNDEFINED;
			if (leftPart == null)
			{
				leftPart= new HashSet<String>();
			}
			if (rightPart == null)
			{
				rightPart= new HashSet<String>();
			}
			if (orientation != null)
			{
				String d = orientation.iterator().next();
				if(d.equals(LR))
				{
					dir = ReactionUtil.Orientation.LR;
				}
				if(d.equals(RL))
				{
					dir = ReactionUtil.Orientation.RL;
				}
				if(d.equals(BOTH))
				{
					dir = ReactionUtil.Orientation.BOTH;
				}
			}
			Reaction<String> r = new Reaction<String>(entry.getKey(), leftPart, rightPart, dir);
			if (r.isValid())
			{
				meta.addReaction(r);
				Collection<String> pws = entry.getValue().get(IN_PATHWAY);
				if (pws != null)
				{
					for (String p : pws)
					{
						Collection<Reaction<String>> rSet = pathways.get(p);
						if (rSet == null)
						{
							rSet = new HashSet<Reaction<String>>();
							pathways.put(p, rSet);
						}
						rSet.add(r);
					}
				}
			}
		}
		Map<String, Reaction<String>> idToReaction= new HashMap<String, Reaction<String>>();
		for (Reaction<String> r :meta.getReactionSet())
		{
			idToReaction.put(r.getId(), r);
		}
		for (Entry<String, Map<String, Collection<String>>> entry : enzrxns.entrySet())
		{
			Collection<String> reacts = entry.getValue().get(REACTION);
			Collection<String> enzymes = entry.getValue().get(ENZYME);
			if (reacts != null && enzymes != null)
			{
				for (String r : reacts)
				{
					for (String enz : enzymes)
					{
						Protein p = idToProtein.get(enz);
						meta.addCatalyze(p,idToReaction.get(r));	
					}
				}
			}
		}
		compounds.clear();
		reactions.clear();
		enzrxns.clear();
	}


	@Override
	public MetabolicNetworkWithEnzymes<String, Reaction<String>,Protein> get() {
		return this.meta;
	}


	@Override
	public Map<String, Collection<Reaction<String>>> getPathways() {
		return this.pathways;
	}

	public static void main(String[] args) {
		int filter = 80;
		String profilFile = args[0];
		String outMetabolicGraphFile = args[1];
		String outReactionGraphFile = args[2];
		String outCompoundGraphFile = args[3];
		try {
			Profil ecocycFiles = Profil.createProfil(profilFile);
			IOMetabolicNetworkWithEnzymes<String, Reaction<String>,Protein> metaIn;
			metaIn = new IOMetabolicNetworkWithEnzymesFromMetaCyc(ecocycFiles.get("ecocyc_files_path")+ ecocycFiles.get("compounds"),
					ecocycFiles.get("ecocyc_files_path")+ ecocycFiles.get("reactions"),
					ecocycFiles.get("ecocyc_files_path")+ ecocycFiles.get("enzrxns"),
					ecocycFiles.get("ecocyc_files_path")+ ecocycFiles.get("proteins"));

			long oldtime = System.currentTimeMillis();
			System.out.println("Loading data from the files :\n" + ecocycFiles.get("ecocyc_files_path")+ ecocycFiles.get("compounds")
					+ "\n" + ecocycFiles.get("ecocyc_files_path")+ ecocycFiles.get("reactions"));
			metaIn.load();
			long newtime = System.currentTimeMillis();
			System.out.println("the process tooks " + (newtime - oldtime) + "ms");

			System.out.println(metaIn.get().getCompoundSet().size() + "compounds");
			System.out.println(metaIn.get().getReactionSet().size() + "reactions");
			System.out.println(metaIn.get().getEnzymeSet().size() + "enzymes");


			System.out.println("Generating filtered metabolic network with <= "+filter+" compounds");
			oldtime = System.currentTimeMillis();
			combi.wip.sipper.modeling.metabolicNetworks.MetabolicNetworkUtils<String, Reaction<String>> utils = 
					new combi.wip.sipper.modeling.metabolicNetworks.MetabolicNetworkUtils<String, Reaction<String>>();

			Collection<String> filterCPD = new HashSet<String>();
			for(Entry<String, Integer> entry : utils.compoundsCount(metaIn.get()).entrySet())
			{
				if (entry.getValue()>filter)
				{
					filterCPD.add(entry.getKey());
				}
			}
			ImplOfFilteredMetabolicNetworkWithEnzymes<String, Reaction<String>, Protein> metaEnz = new ImplOfFilteredMetabolicNetworkWithEnzymes<String, Reaction<String>, Protein>(metaIn.get(), filterCPD);
			System.out.println(metaEnz.getCompoundSet().size() + "compounds");
			System.out.println(metaEnz.getReactionSet().size() + "reactions");
			System.out.println(metaEnz.getEnzymeSet().size() + "enzymes");
			System.out.println(filterCPD.size() + "compounds to be filtered\n" 
					+ filterCPD);
			System.out.println(metaEnz.getRemovedReactions().size() + "removed reactions\n" 
					+ metaEnz.getRemovedReactions());
			newtime = System.currentTimeMillis();
			System.out.println("the process tooks " + (newtime - oldtime) + "ms");

			oldtime = System.currentTimeMillis();
			System.out.println("Generating metabolic graph");
			org.jgrapht.ext.DOTExporter<String, DefaultEdge> outGraphM = new org.jgrapht.ext.DOTExporter<String, DefaultEdge> (new org.jgrapht.ext.IntegerNameProvider<String>(), new org.jgrapht.ext.StringNameProvider<String>(),null);
			combi.wip.sipper.modeling.metabolicNetwork.graphs.MetabolicGraph<String, Reaction<String>> graphM = new combi.wip.sipper.modeling.metabolicNetwork.graphs.MetabolicGraph<String, Reaction<String>>(metaEnz);			
			outGraphM.export(new java.io.BufferedWriter(new java.io.FileWriter(outMetabolicGraphFile)), graphM.generateGraph());

			newtime = System.currentTimeMillis();
			System.out.println("the process tooks " + (newtime - oldtime) + "ms");
			oldtime = System.currentTimeMillis();

			System.out.println("Generating reaction graph");
			org.jgrapht.ext.DOTExporter<String, DefaultEdge> outGraphR = new org.jgrapht.ext.DOTExporter<String, DefaultEdge> (new org.jgrapht.ext.IntegerNameProvider<String>(), new org.jgrapht.ext.StringNameProvider<String>(),null);
			combi.wip.sipper.modeling.metabolicNetwork.graphs.ReactionGraph<String, Reaction<String>> graphR = new combi.wip.sipper.modeling.metabolicNetwork.graphs.ReactionGraph<String, Reaction<String>>(metaEnz);
			outGraphR.export(new java.io.BufferedWriter(new java.io.FileWriter(outReactionGraphFile)), graphR.generateGraph());

			newtime = System.currentTimeMillis();
			System.out.println("the process tooks " + (newtime - oldtime) + "ms");
			oldtime = System.currentTimeMillis();

			System.out.println("Generating compound graph");
			org.jgrapht.ext.DOTExporter<String, DefaultEdge> outGraphC = new org.jgrapht.ext.DOTExporter<String, DefaultEdge> (new org.jgrapht.ext.IntegerNameProvider<String>(), new org.jgrapht.ext.StringNameProvider<String>(),null);
			combi.wip.sipper.modeling.metabolicNetwork.graphs.CompoundGraph<String, Reaction<String>> graphC = new combi.wip.sipper.modeling.metabolicNetwork.graphs.CompoundGraph<String, Reaction<String>>(metaEnz);
			outGraphC.export(new java.io.BufferedWriter(new java.io.FileWriter(outCompoundGraphFile)), graphC.generateGraph());

			newtime = System.currentTimeMillis();
			System.out.println("the process tooks " + (newtime - oldtime) + "ms");

			oldtime = System.currentTimeMillis();
			System.out.println("Generating pathways and pathways graph");
			System.out.println(metaIn.getPathways().keySet());
			for (Entry<String, Collection<Reaction<String>>> entry : metaIn.getPathways().entrySet())
			{
				MetabolicNetworkWithEnzymes<String, Reaction<String>, Protein> pw = new ImplOfFilteredMetabolicNetworkWithEnzymes<String, Reaction<String>, Protein>(new Pathway<String, Reaction<String>, Protein>(metaEnz,entry.getValue()),filterCPD);
				combi.wip.sipper.modeling.metabolicNetwork.graphs.ReactionGraph<String, Reaction<String>> graphPWR = new combi.wip.sipper.modeling.metabolicNetwork.graphs.ReactionGraph<String, Reaction<String>>(pw);
				outGraphR.export(new java.io.BufferedWriter(new java.io.FileWriter("PWY/graphes_reactions/"+entry.getKey()+".dot")), graphPWR.generateGraph());
				combi.wip.sipper.modeling.metabolicNetwork.graphs.MetabolicGraph<String, Reaction<String>> graphPWM = new combi.wip.sipper.modeling.metabolicNetwork.graphs.MetabolicGraph<String, Reaction<String>>(pw);
				outGraphR.export(new java.io.BufferedWriter(new java.io.FileWriter("PWY/graphes_metaboliques/"+entry.getKey()+".dot")), graphPWM.generateGraph());
			}
			newtime = System.currentTimeMillis();
			System.out.println("the process tooks " + (newtime - oldtime) + "ms");

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
